package com.io.nio2;

import io.netty.buffer.ByteBuf;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * @author hjj
 * @create 2019-11-24 19:21
 */
public class NIOClientHandler implements Runnable {
    private String host;
    private Integer port;
    private Selector selector;
    private SocketChannel socketChannel;
    private volatile boolean started;
    private final int BUFFER_SIZE = 1024;

    public NIOClientHandler(String host, Integer port) {
        this.host = host;
        this.port = port;
        try {
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            started = true;
            System.out.println("NIO client start >>> port: " + port);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void stop() {
        started = false;
    }

    @Override
    public void run() {
        try {
            socketChannel.connect(new InetSocketAddress(host, port));
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            while (started) {
                selector.select();
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                SelectionKey key;
                while (iterator.hasNext()) {
                    key = iterator.next();
                    iterator.remove();
                    handleInput(key);
                }
            }
            if (selector != null) {
                try {
                    selector.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void handleInput(SelectionKey key) throws Exception {
        if (!key.isValid()) return;
        SocketChannel socketChannel = (SocketChannel) key.channel();
        if (key.isConnectable() && !socketChannel.finishConnect()) System.exit(1);
        if (key.isReadable()) read(key);
        if (key.isWritable()) write(key);
    }

    public void read(SelectionKey key) throws Exception {
        ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER_SIZE);
        int readBytes = socketChannel.read(readBuffer);
        if (readBytes > 0) {
            readBuffer.flip();
            byte[] bytes = new byte[readBuffer.remaining()];
            readBuffer.get(bytes);
            System.out.println("NIO client received: " + new String(bytes, "UTF-8"));
        } else if (readBytes < 0) {
            key.channel();
            socketChannel.close();
        }
    }

    public void write(SelectionKey key) throws Exception {
        ByteBuffer writeBuffer = ByteBuffer.allocate(BUFFER_SIZE);
        int writeBytes = socketChannel.write(writeBuffer);
        if (writeBytes > 0) {
            writeBuffer.flip();
            byte[] bytes = new byte[writeBuffer.remaining()];
            writeBuffer.get(bytes);
            send(bytes);
        } else if (writeBytes < 0) {
            key.cancel();
            socketChannel.close();
        }
    }

    public void send(String msg) throws Exception {
//        socketChannel.register(selector, SelectionKey.OP_WRITE);
        send(msg.getBytes());
    }

    public void send(byte[] bytes) throws Exception {
        if (bytes.length == 0) return;
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        writeBuffer.put(bytes);
        writeBuffer.flip();
        socketChannel.write(writeBuffer);
        System.out.println("NIO client send: " + new String(bytes, "UTF-8"));
    }
}
